package texas

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/heroiclabs/nakama-common/runtime"
	"math/rand"
	"strconv"
)

type GameStage int

const (
	Waiting  GameStage = iota // 等待玩家加入
	PreFlop                   // 翻牌前
	Flop                      // 翻牌
	Turn                      // 转牌
	River                     // 河牌
	Showdown                  // 摊牌
	GameOver                  // 游戏结束
)

type TexasHoldemMatch struct {
	logger runtime.Logger
	db     *sql.DB
	nk     runtime.NakamaModule

	// 游戏状态
	state      *TexasHoldemState
	dispatcher runtime.MatchDispatcher
}

type TexasHoldemState struct {
	Players        map[string]*Player // 玩家列表
	Deck           []Card             // 牌堆
	CommunityCards []Card             // 公共牌
	Pot            int                // 奖池金额
	CurrentBet     int                // 当前注额
	DealerPos      int                // 庄家位置
	CurrentPos     int                // 当前行动玩家位置
	Stage          GameStage          // 游戏阶段
}

// MatchInit 匹配初始化
func (m *TexasHoldemMatch) MatchInit(
	ctx context.Context,
	logger runtime.Logger,
	db *sql.DB,
	nk runtime.NakamaModule,
	params map[string]interface{},
) (interface{}, int, string) {
	m.logger = logger
	m.db = db
	m.nk = nk

	// 初始化游戏状态
	state := &TexasHoldemState{
		Players:        make(map[string]*Player),
		Deck:           NewShuffledDeck(),
		CommunityCards: make([]Card, 0),
		Pot:            0,
		CurrentBet:     0,
		DealerPos:      -1,
		CurrentPos:     -1,
		Stage:          Waiting,
	}

	m.state = state

	// 设置匹配标签(显示在匹配列表中)
	label := "Texas Hold'em - Waiting for players"

	// 返回初始状态，设置tick率为每秒5次
	return state, 5, label
}

func (m *TexasHoldemMatch) MatchJoinAttempt(
	ctx context.Context,
	logger runtime.Logger,
	db *sql.DB, nk runtime.NakamaModule,
	dispatcher runtime.MatchDispatcher,
	tick int64, state interface{},
	presence runtime.Presence,
	metadata map[string]string,
) (interface{}, bool, string) {
	s := state.(*TexasHoldemState)
	// 检查游戏是否已开始
	if s.Stage != Waiting {
		return s, false, "Game already started"
	}
	// 检查玩家人数(德州扑克通常2-10人)
	if len(s.Players) >= 10 {
		return s, false, "Game is full"
	}

	// 检查筹码购买量
	buyIn, ok := metadata["buy_in"]
	if !ok {
		return s, false, "Buy in amount required"
	}

	buyInAmount, err := strconv.Atoi(buyIn)
	if err != nil || buyInAmount < 1000 || buyInAmount > 10000 {
		return s, false, "Invalid buy in amount"
	}

	return s, true, ""
}

func (m *TexasHoldemMatch) MatchJoin(
	ctx context.Context,
	logger runtime.Logger,
	db *sql.DB,
	nk runtime.NakamaModule,
	dispatcher runtime.MatchDispatcher,
	tick int64,
	state interface{},
	presences []runtime.Presence,
) interface{} {

	buyInAmount := 1000

	s := state.(*TexasHoldemState)
	for _, p := range presences {

		player := &Player{
			UserID:    p.GetUserId(),
			SessionID: p.GetSessionId(),
			Chips:     buyInAmount,
			Hand:      make([]Card, 0),
			Bet:       0,
			Folded:    false,
			AllIn:     false,
		}

		s.Players[p.GetUserId()] = player

		// 通知所有玩家有新玩家加入
		dispatcher.BroadcastMessage(
			1,
			[]byte(
				fmt.Sprintf(
					`{"type":"player_joined","user_id":"%s", "chips":%d}`,
					p.GetUserId(),
					buyInAmount,
				),
			),
			nil,
			nil,
			true,
		)
	}

	// 如果有足够玩家(如2人)，开始游戏
	if len(s.Players) >= 2 && s.Stage == Waiting {
		m.startGame(dispatcher)
	}

	return state
}

func (m *TexasHoldemMatch) startGame(dispatcher runtime.MatchDispatcher) {
	s := m.state

	// 设置庄家位置（首次随机）
	playerIDs := make([]string, 0, len(s.Players))
	for id := range s.Players {
		playerIDs = append(playerIDs, id)
	}
	s.DealerPos = rand.Intn(len(playerIDs))
}

//// nextActiveSeat 返回从 startPos 顺时针数第 offset(+1, +2, …) 位有人的座位号。
//// 如果桌上玩家人数不足 offset，返回 -1。
//func nextActiveSeat(startPos SeatID, offset int, seated Seats) SeatID {
//	if len(seated) == 0 || offset <= 0 {
//		return -1
//	}
//	steps := 0
//	pos := (int(startPos) + 1) % 9 // 从下一位开始找
//	for searched := 0; searched < 9; searched++ {
//		if _, ok := seated[SeatID(pos)]; ok { // 该座位有人
//			steps++
//			if steps == offset {
//				return SeatID(pos)
//			}
//		}
//		pos = (pos + 1) % 9
//	}
//	return -1 // 不足 offset 位玩家
//}
//
//// CalcBlindsPreflop 返回小盲、大盲、Pre-flop 第一行动位。
//// 若玩家不足 -> 对应返回 -1。
//func CalcBlindsPreflop(dealerPos SeatID, seated Seats) (sb SeatID, bb SeatID, firstToAct SeatID) {
//	sb = nextActiveSeat(dealerPos, 1, seated)         // dealer 左边第一活跃玩家
//	bb = nextActiveSeat(dealerPos, 2, seated)         // dealer 左边第二活跃玩家
//	firstToAct = nextActiveSeat(dealerPos, 3, seated) // Pre-flop 第三位
//	return
//}
